﻿using System.Collections.Generic;
using System.IO;
using Gameplay.PVE.Survivor;
using UnityEditor.Animations;
using UnityEngine;
using Yoozoo.Gameplay.RTS;

namespace GameFramework.Battle.Tools
{
    using Yoozoo.Gameplay.City.GpuSkinning.Editor;

    public class GPUInstancedAnimationGenerator
    {
        /// <summary>
        /// 挂点
        /// </summary>
        public string[] mountPoints
        {
            get
            {
                if (GpuSkinningAnimatorCreator.isSurvivor)
                {
                    return survivorPoints;
                }
                else
                {
                    return cityMountPoints;
                }
            }
        }
        public string[] cityMountPoints = new[]
        {
            "Bip001 R Hand",
            "Bip001 Head",
            "Bip001 L Hand",
        };

        public string[] survivorPoints = new[]
        {
            "Bip001 Head",
            //"BN_Weapon",
        };

        static void Tips(string tile, string strLog)
        {
            Debug.LogWarning(strLog);
        }

        static void Warning(string tile, string strLog)
        {
            UnityEngine.Debug.LogError(strLog);

#if DISPLAY_DIALOG
            EditorUtility.DisplayDialog(tile, strLog , "OK");
#endif
        }

        bool CompareApproximately(float f0, float f1, float epsilon = 0.000001F)
        {
            float dist = (f0 - f1);
            dist = Mathf.Abs(dist);
            return dist < epsilon;
        }

        Quaternion ToQuaternion(Matrix4x4 mat)
        {
            float det = mat.determinant;
            if (!CompareApproximately(det, 1.0F, .005f))
                return Quaternion.identity;

            Quaternion quat = Quaternion.identity;
            float tr = mat.m00 + mat.m11 + mat.m22;

            // check the diagonal
            if (tr > 0.0f)
            {
                float fRoot = Mathf.Sqrt(tr + 1.0f);  // 2w
                quat.w = 0.5f * fRoot;
                fRoot = 0.5f / fRoot;  // 1/(4w)
                quat.x = (mat[2, 1] - mat[1, 2]) * fRoot;
                quat.y = (mat[0, 2] - mat[2, 0]) * fRoot;
                quat.z = (mat[1, 0] - mat[0, 1]) * fRoot;
            }
            else
            {
                // |w| <= 1/2
                int[] s_iNext = { 1, 2, 0 };
                int i = 0;
                if (mat.m11 > mat.m00)
                    i = 1;
                if (mat.m22 > mat[i, i])
                    i = 2;
                int j = s_iNext[i];
                int k = s_iNext[j];

                float fRoot = Mathf.Sqrt(mat[i, i] - mat[j, j] - mat[k, k] + 1.0f);
                if (fRoot < float.Epsilon)
                    return Quaternion.identity;

                quat[i] = 0.5f * fRoot;
                fRoot = 0.5f / fRoot;
                quat.w = (mat[k, j] - mat[j, k]) * fRoot;
                quat[j] = (mat[j, i] + mat[i, j]) * fRoot;
                quat[k] = (mat[k, i] + mat[i, k]) * fRoot;
            }

            return QuaternionNormalize(quat);

        }

        public static Quaternion QuaternionNormalize(Quaternion quat)
        {
            float scale = new Vector4(quat.x, quat.y, quat.z, quat.w).magnitude;
            scale = 1.0f / scale;

            return new Quaternion(scale * quat.x, scale * quat.y, scale * quat.z, scale * quat.w);
        }

        public class DualQuaternion_c
        {
            public Quaternion m_real;
            public Quaternion m_dual;
            public static Quaternion Quaternion_Normalize(Quaternion q)
            {
                float mod = Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
                return new Quaternion(q.x / mod, q.y / mod, q.z / mod, q.w / mod);
            }

            public static Quaternion Quaternion_Multiply(Quaternion a, Quaternion b)
            {
                Quaternion ret = new Quaternion();
                ret.w = (a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z);
                ret.x = (a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y);
                ret.y = (a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x);
                ret.z = (a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w);
                return ret;
            }

            public static Quaternion Quaternion_Multiply(Quaternion a, float b)
            {
                return new Quaternion(a.x * b, a.y * b, a.z * b, a.w * b);
            }

            public static Quaternion Quaternion_Conjugate(Quaternion a)
            {
                return new Quaternion(-a.x, -a.y, -a.z, a.w);
            }

            public static Quaternion Quaternion_Add(Quaternion a, Quaternion b)
            {
                Quaternion ret = new Quaternion(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
                return ret;
            }

            public DualQuaternion_c()
            {
                m_real = new Quaternion(0, 0, 0, 1);
                m_dual = new Quaternion(0, 0, 0, 0);
            }
            public DualQuaternion_c(Quaternion r, Quaternion d)
            {
                m_real = Quaternion_Normalize(r);
                m_dual = d;
            }
            public DualQuaternion_c(Quaternion r, Vector3 t)
            {
                m_real = Quaternion_Normalize(r);
                m_dual = Quaternion_Multiply(Quaternion_Multiply(new Quaternion(t.x, t.y, t.z, 0), m_real), 0.5f);
            }
            public static float Dot(DualQuaternion_c a, DualQuaternion_c b)
            {
                return Quaternion.Dot(a.m_real, b.m_real);
            }
            public static DualQuaternion_c operator *(DualQuaternion_c q, float scale)
            {
                DualQuaternion_c ret = q;
                ret.m_real = Quaternion_Multiply(ret.m_real, scale);
                ret.m_dual = Quaternion_Multiply(ret.m_dual, scale);
                return ret;
            }
            public static DualQuaternion_c Normalize(DualQuaternion_c q)
            {
                float mag = Quaternion.Dot(q.m_real, q.m_real);
                DualQuaternion_c ret = q;
                ret.m_real = Quaternion_Multiply(ret.m_real, 1.0f / mag);
                ret.m_dual = Quaternion_Multiply(ret.m_dual, 1.0f / mag);
                return ret;
            }

            public static DualQuaternion_c operator +(DualQuaternion_c lhs, DualQuaternion_c rhs)
            {
                return new DualQuaternion_c(Quaternion_Add(lhs.m_real, rhs.m_real), Quaternion_Add(lhs.m_dual, rhs.m_dual));
            }
            public static DualQuaternion_c operator *(DualQuaternion_c lhs, DualQuaternion_c rhs)
            {
                return new DualQuaternion_c(Quaternion_Multiply(rhs.m_real, lhs.m_real), Quaternion_Add(Quaternion_Multiply(rhs.m_dual, lhs.m_real), Quaternion_Multiply(rhs.m_real, lhs.m_dual)));
            }
            public static DualQuaternion_c Conjugate(DualQuaternion_c q)
            {
                return new DualQuaternion_c(Quaternion_Conjugate(q.m_real), Quaternion_Conjugate(q.m_dual));
            }
            public static Quaternion GetRotation(DualQuaternion_c q)
            {
                return q.m_real;
            }
            public static Vector3 GetTranslation(DualQuaternion_c q)
            {
                Quaternion t = Quaternion_Multiply(Quaternion_Multiply(q.m_dual, 2.0f), Quaternion_Conjugate(q.m_real));
                return new Vector3(t.x, t.y, t.z);
            }
            public static Matrix4x4 DualQuaternionToMatrix(DualQuaternion_c q)
            {
                q = DualQuaternion_c.Normalize(q);

                Matrix4x4 M = Matrix4x4.identity;
                float w = q.m_real.w;
                float x = q.m_real.x;
                float y = q.m_real.y;
                float z = q.m_real.z;

                // Extract rotational information   
                M.m00 = w * w + x * x - y * y - z * z;
                M.m10 = 2 * x * y + 2 * w * z;
                M.m20 = 2 * x * z - 2 * w * y;
                M.m01 = 2 * x * y - 2 * w * z;
                M.m11 = w * w + y * y - x * x - z * z;
                M.m21 = 2 * y * z + 2 * w * x;

                M.m02 = 2 * x * z + 2 * w * y;
                M.m12 = 2 * y * z - 2 * w * x;
                M.m22 = w * w + z * z - x * x - y * y;

                // Extract translation information   
                Quaternion t = Quaternion_Multiply(Quaternion_Multiply(q.m_dual, 2.0f), Quaternion_Conjugate(q.m_real));
                M.m03 = t.x;
                M.m13 = t.y;
                M.m23 = t.z;
                return M;
            }
        }

        public int m_bakeFrame = 60;
        public string prefabFolderPath;
        private List<GPUSkinning_StateParameter> m_parameters = new List<GPUSkinning_StateParameter>();
        private List<AnimatorState> m_stateAdded = new List<AnimatorState>();
        private List<GPUSkinningAnimatorState> m_stateStoredAdded = new List<GPUSkinningAnimatorState>();
        private List<GPUSkinningBoneAnimationTransition> m_allTransitions = new List<GPUSkinningBoneAnimationTransition>();
        public GPUSkinning_AllBoneAnimation m_allBoneAnimation = null;
        private GpuSkinningAnimatorCreator gpuSkinningAnimatorCreator;

        public GPUInstancedAnimationGenerator(GpuSkinningAnimatorCreator gpuSkinningAnimatorCreator)
        {
            this.gpuSkinningAnimatorCreator = gpuSkinningAnimatorCreator;
        }

        void AddBones(List<GPUSkinning_Bone> bones, Transform transform, bool parentIsFound, bool parentIsTopNode)
        {
            bool found = false;
            GPUSkinning_Bone targetBone = null;
            foreach (GPUSkinning_Bone bone in bones)
            {
                if (bone.transform == transform)
                {
                    found = true;
                    targetBone = bone;
                    break;
                }
            }
            if (!found)
            {
                GPUSkinning_Bone newBone = new GPUSkinning_Bone();
                newBone.transform = transform;
                newBone.hasNotFoundParentTopNode = !parentIsTopNode;
                bones.Add(newBone);
            }
            else if (parentIsFound == false)
            {
                targetBone.isTopNode = true;
                parentIsTopNode = true;
                targetBone.hasNotFoundParentTopNode = !parentIsTopNode;
            }
            for (int i = 0; i < transform.childCount; i++)
            {
                AddBones(bones, transform.GetChild(i), found, parentIsTopNode);
            }
        }

        Matrix4x4 QuaternionToMatrix(Vector4 vec)
        {
            Matrix4x4 matrix = new Matrix4x4();
            matrix[0, 0] = 2 * (vec.x * vec.x + vec.w * vec.w) - 1;
            matrix[1, 0] = 2 * (vec.x * vec.y + vec.z * vec.w);
            matrix[2, 0] = 2 * (vec.x * vec.z - vec.y * vec.w);
            matrix[3, 0] = 0.0f;
            matrix[0, 1] = 2 * (vec.x * vec.y - vec.z * vec.w);
            matrix[1, 1] = 2 * (vec.y * vec.y + vec.w * vec.w) - 1;
            matrix[2, 1] = 2 * (vec.y * vec.z + vec.x * vec.w);
            matrix[3, 1] = 0.0f;
            matrix[0, 2] = 2 * (vec.x * vec.z + vec.y * vec.w);
            matrix[1, 2] = 2 * (vec.y * vec.z - vec.x * vec.w);
            matrix[2, 2] = 2 * (vec.z * vec.z + vec.w * vec.w) - 1;
            matrix[3, 2] = 0.0f;
            matrix[0, 3] = 0.0f;
            matrix[1, 3] = 0.0f;
            matrix[2, 3] = 0.0f;
            matrix[3, 3] = 1.0f;
            return matrix;
        }

        Matrix4x4 LerpMatrix4x4(Matrix4x4 fromMatrix, Matrix4x4 toMatrix, float progress)
        {
            Matrix4x4 retMatrix = new Matrix4x4();
            retMatrix.m00 = fromMatrix.m00 * (1 - progress) + toMatrix.m00 * progress;
            retMatrix.m01 = fromMatrix.m01 * (1 - progress) + toMatrix.m01 * progress;
            retMatrix.m02 = fromMatrix.m02 * (1 - progress) + toMatrix.m02 * progress;
            retMatrix.m03 = fromMatrix.m03 * (1 - progress) + toMatrix.m03 * progress;
            retMatrix.m10 = fromMatrix.m10 * (1 - progress) + toMatrix.m10 * progress;
            retMatrix.m11 = fromMatrix.m11 * (1 - progress) + toMatrix.m11 * progress;
            retMatrix.m12 = fromMatrix.m12 * (1 - progress) + toMatrix.m12 * progress;
            retMatrix.m13 = fromMatrix.m13 * (1 - progress) + toMatrix.m13 * progress;
            retMatrix.m20 = fromMatrix.m20 * (1 - progress) + toMatrix.m20 * progress;
            retMatrix.m21 = fromMatrix.m21 * (1 - progress) + toMatrix.m21 * progress;
            retMatrix.m22 = fromMatrix.m22 * (1 - progress) + toMatrix.m22 * progress;
            retMatrix.m23 = fromMatrix.m23 * (1 - progress) + toMatrix.m23 * progress;
            retMatrix.m30 = fromMatrix.m30 * (1 - progress) + toMatrix.m30 * progress;
            retMatrix.m31 = fromMatrix.m31 * (1 - progress) + toMatrix.m31 * progress;
            retMatrix.m32 = fromMatrix.m32 * (1 - progress) + toMatrix.m32 * progress;
            retMatrix.m33 = fromMatrix.m33 * (1 - progress) + toMatrix.m33 * progress;
            return retMatrix;
        }

        private void RecursiveTransitionConditionAddState(List<AnimatorStateMachine> stateMachineStack, AnimatorState state, AnimatorStateTransition inTran, List<GPUSkinningAnimatorTransition> transitionList = null)
        {
            float duration = inTran != null ? inTran.duration : 0;

            float offset = inTran != null ? inTran.offset : 0;
            int interruptionSource = inTran != null ? (int)inTran.interruptionSource : -1;
            bool orderedInterruption = inTran != null ? inTran.orderedInterruption : false;

            bool fixedDuration = inTran != null ? inTran.hasFixedDuration : false;
            bool hasExitTime = inTran != null ? inTran.hasExitTime : true;
            float exitTime = inTran != null ? inTran.exitTime : 0;
            bool canTransitionToSelf = inTran != null ? inTran.canTransitionToSelf : false;
            AnimatorCondition[] conditions = inTran != null ? inTran.conditions : null;

            //Only transitions with source state and dest state will be added in transition List, else will be released automatically
            GPUSkinningAnimatorTransition transitionStored = new GPUSkinningAnimatorTransition();
            transitionStored.m_HasExitTime = hasExitTime;
            transitionStored.m_ExitTime = exitTime;
            transitionStored.m_FixedDuration = fixedDuration;
            transitionStored.m_Offset = offset;
            transitionStored.m_InterruptionSource = interruptionSource;
            transitionStored.m_OrderedInterruption = orderedInterruption;
            transitionStored.m_Duration = duration;
            if (conditions != null){
                transitionStored.m_ConditionHashes = new int[conditions.Length];
                int index = 0;
                foreach (var condition in conditions)
                {
                    transitionStored.m_ConditionHashes[index++] = m_parameters.FindIndex(x => x.hashCode == condition.parameter.GetHashCode()); 
                }
            }
            else
            {
                transitionStored.m_ConditionHashes = new int[0];
            }
            if (m_stateAdded.Contains(state))
            {
                transitionStored.m_TargetStateIndex = m_stateAdded.IndexOf(state);
            }
            else
            {
                var animatorState = new GPUSkinningAnimatorState();
                animatorState.m_Name = state.name;
                animatorState.m_MotionIndex = -1;
                if (state.motion is AnimationClip)
                {
                    AnimationClip clip = state.motion as AnimationClip;

                    animatorState.m_MotionIndex = gpuSkinningAnimatorCreator.GetMotionIndex(clip);


                    if (animatorState.m_MotionIndex == -1 )
                        UnityEngine.Debug.LogError("not found AnimationClip." + state.name);

                    var motion = gpuSkinningAnimatorCreator.GetOverrideMotion(clip);
                    if (motion == null)
                    {
                        motion = state.motion;
                    }
                    animatorState.m_IsLooping = motion.isLooping;
                }
                else  if (state.motion is BlendTree)
                {
                    UnityEngine.Debug.LogError("only support AnimationClip." + state.name);
                }

                animatorState.transitions = new List<GPUSkinningAnimatorTransition>();
                m_stateAdded.Add(state);
                m_stateStoredAdded.Add(animatorState);
                transitionStored.m_TargetStateIndex = m_stateAdded.IndexOf(state);
                transitionStored.m_CanTransitionToSelf = canTransitionToSelf;
                foreach (var transition in state.transitions)
                {
                    RecursiveTransitionConditionAddState(stateMachineStack, transition.destinationState, transition, animatorState.transitions);
                }
                List<AnimatorStateTransition> transitions = new List<AnimatorStateTransition>();
                GetAllAnyTransitions(stateMachineStack, transitions);
                foreach (var transition in transitions)
                {
                    RecursiveTransitionConditionAddState(stateMachineStack, transition.destinationState, transition, animatorState.transitions);
                }
            }
            if (transitionList != null)
            {
                transitionList.Add(transitionStored);
            }
        }

        private string GetDefaultStateName(AnimatorStateMachine sm)
        {
            if (sm.defaultState != null)
            {
                return sm.defaultState.name;
            }

            foreach (var childAnimatorStateMachine in sm.stateMachines)
            {
                if (childAnimatorStateMachine.stateMachine.defaultState != null)
                {
                    return childAnimatorStateMachine.stateMachine.defaultState.name;
                }
            }

            return "";
        }

        private void RecursiveTransitionAllStates(List<AnimatorStateMachine> stateMachineStack)
        {
            var animatorStateMachine = stateMachineStack[stateMachineStack.Count - 1];
            var childStates = animatorStateMachine.states;
            foreach (var childAnimatorState in childStates)
            {
                var state = childAnimatorState.state;
                RecursiveTransitionConditionAddState(stateMachineStack, state, null);
            }

            foreach (var childAnimatorStateMachine in animatorStateMachine.stateMachines)
            {
                stateMachineStack.Add(childAnimatorStateMachine.stateMachine);
                RecursiveTransitionAllStates(stateMachineStack);
                stateMachineStack.RemoveAt(stateMachineStack.Count - 1);
            }
        }

        private void GetAllAnyTransitions(List<AnimatorStateMachine> stateMachineStack, List<AnimatorStateTransition> anyTransitions)
        {
            var animatorStateMachine = stateMachineStack[0];
            var stateMachineList = new List<AnimatorStateMachine>();
            var queue = new Queue<AnimatorStateMachine>();
            queue.Enqueue(animatorStateMachine);

            while (queue.Count > 0)
            {
                var stateMachine = queue.Dequeue();
                stateMachineList.Add(stateMachine);
                foreach (var childAnimatorStateMachine in stateMachine.stateMachines)
                {
                    queue.Enqueue(childAnimatorStateMachine.stateMachine);
                }
            }
            
            foreach (var stateMachine in stateMachineList)
            {
                anyTransitions.AddRange(stateMachine.anyStateTransitions);
            }
        }

        //Get animation fusion info recursively
        private void RecursiveTransitionCondition(AnimatorStateMachine baseSm, AnimatorStateMachine sm)
        {
            ChildAnimatorState[] childStates = sm.states;
            for (int i = 0; i < childStates.Length; i++)
            {
                AnimatorState state = childStates[i].state;
                AnimatorStateTransition[] transitions = state.transitions;
                for (int j = 0; j < transitions.Length; j++)
                {
                    AnimatorStateTransition transition = transitions[j];
                    GPUSkinningBoneAnimationTransition transitionStored = new GPUSkinningBoneAnimationTransition();

                    transitionStored.m_Source = state.name;
                    transitionStored.m_Target = transition.destinationState.name;
                    //if (transitionStored.m_Source == transitionStored.m_Target)
                    //    continue;
                    
                    transitionStored.m_Duration = transition.duration;
                    transitionStored.m_Offset = transition.offset;
                    transitionStored.m_ExitTime = transition.exitTime;

                    m_allTransitions.Add(transitionStored);
                }
                List<AnimatorStateTransition> allAnyTransitions = new List<AnimatorStateTransition>();
                GetAllAnyTransitions(new List<AnimatorStateMachine>{ baseSm }, allAnyTransitions);
                for (int j = 0; j < allAnyTransitions.Count; j++)
                {
                    AnimatorStateTransition transition = allAnyTransitions[j];
                    GPUSkinningBoneAnimationTransition transitionStored = new GPUSkinningBoneAnimationTransition();

                    transitionStored.m_Source = state.name;
                    transitionStored.m_Target = transition.destinationState.name;
                    //if (transitionStored.m_Source == transitionStored.m_Target)
                    //    continue;
 
                    transitionStored.m_Duration = transition.duration;
                    transitionStored.m_Offset = transition.offset;
                    transitionStored.m_ExitTime = transition.exitTime;

                    m_allTransitions.Add(transitionStored);
                }
            }
            ChildAnimatorStateMachine[] childSms = sm.stateMachines;
            for (int i = 0; i < childSms.Length; i++)
            {
                RecursiveTransitionCondition(baseSm, childSms[i].stateMachine);
            }
        }

        private void AnimatorParameters(AnimatorController animator)
        {
            AnimatorControllerParameter[] parameters = animator.parameters;
            for (int i = 0; i < parameters.Length; i++)
            {
                AnimatorControllerParameter param = parameters[i];
                GPUSkinning_StateParameter gpuParam = new GPUSkinning_StateParameter();
                gpuParam.name = param.name;
                gpuParam.hashCode = param.name.GetHashCode();
                gpuParam.value = param.defaultFloat;
                m_parameters.Add(gpuParam);
            }
        }

        private string[] GetMountPoints(GameObject obj)
        {
            var mountPoints = new List<string>();
            for (int i = 0; i < this.mountPoints.Length; i++)
            {
                mountPoints.Add(this.mountPoints[i]);
            }

            if (obj != null)
            {
                var weapon = obj.GetComponent<GpuSkinningWeapon>();
                if (weapon != null)
                {
                    for (int i = 0; i < weapon.weaponList.Count; i++)
                    {
                        if (!mountPoints.Contains(weapon.weaponList[i].point.name))
                        {
                            mountPoints.Add(weapon.weaponList[i].point.name);
                        }
                    }
                }
            }
            return mountPoints.ToArray();
        }

        public void ToGenerate(string animationDirectory, string textureDataDirectory, string assetName, GameObject obj, List<SkinnedMeshRenderer> validSkinnedMeshRenderers, RuntimeAnimatorController animatorController, Transform[] boneList,GameObject originPrefab = null)
        {
            m_stateAdded.Clear();
            m_stateStoredAdded.Clear();

            string animInstancePath = Path.Combine(animationDirectory, assetName) + ".asset";
            if (string.IsNullOrEmpty(animInstancePath))
            {
                UnityEngine.Debug.LogError("filePath IsNullOrEmpty.");
                return;
            }

            m_allBoneAnimation = ScriptableObject.CreateInstance<GPUSkinning_AllBoneAnimation>();
            m_allBoneAnimation.name = assetName;
            m_allBoneAnimation.mountPoints = GetMountPoints(originPrefab);
            if (animatorController == null)
            {
                UnityEngine.Debug.LogError("Don't has Animator component  " + obj.name);
                return;
            }

            AnimatorController originalAnimatorController = null;
            if (animatorController is AnimatorOverrideController aoc)
            {
                originalAnimatorController = aoc.runtimeAnimatorController as AnimatorController;
            }
            else if (animatorController is AnimatorController)
            {
                originalAnimatorController = animatorController as AnimatorController;
            }
            
            if (originalAnimatorController == null)
            {
                UnityEngine.Debug.LogError("originalAnimatorController == null");
                return;
            }

            AnimatorControllerLayer originalControllerLayer = originalAnimatorController.layers[0];
            AnimatorParameters(originalAnimatorController);
            m_allBoneAnimation.parameters = m_parameters.ToArray();
            m_allTransitions = new List<GPUSkinningBoneAnimationTransition>();

            var stateMachineStack = new List<AnimatorStateMachine>()
            {
                originalControllerLayer.stateMachine
            };
            RecursiveTransitionAllStates(stateMachineStack);
            m_allBoneAnimation.m_AllStates = m_stateStoredAdded.ToArray();
            m_allBoneAnimation.defaultStateName = GetDefaultStateName(originalControllerLayer.stateMachine);
            RecursiveTransitionCondition(originalControllerLayer.stateMachine, originalControllerLayer.stateMachine);
            
            UnityEditor.AssetDatabase.CreateAsset(m_allBoneAnimation, animInstancePath);
            UnityEditor.AssetDatabase.Refresh();
        }


        public void ToGenerate2(string animationDirectory, string textureDataDirectory, string configDirectory, string assetName, GameObject obj, SkinnedMeshRenderer skinnedMeshRenderer, Transform[] boneList, List<AnimationClip> animationClipList)
        {
            Matrix4x4 local2World = obj.transform.localToWorldMatrix;
            
            Transform root = skinnedMeshRenderer.rootBone;
            if (root == null)
            {
                UnityEngine.Debug.LogError("Don't has Root: " + obj.name);
                return;
            }

            // Init Bones
            int numBones = boneList.Length;
            var bones = new GPUSkinning_Bone[numBones];
            for (int i = 0; i < numBones; ++i)
            {
                GPUSkinning_Bone bone = new GPUSkinning_Bone();
                bones[i] = bone;
                bone.transform = boneList[i];
                bone.bindpose = boneList[i].worldToLocalMatrix * local2World;
            }

            var allAnimation = ScriptableObject.CreateInstance<GPUSkinning_AllAnimation>();
            allAnimation.boneCount = bones.Length;
            allAnimation.name = assetName;
            
            var gpuSkinningAnimations = new List<GPUSkinning_Animation>();
            for (var clipId = 0; clipId < animationClipList.Count; clipId++)
            {
                var animClip = animationClipList[clipId];
                int frameCount = Mathf.CeilToInt(animClip.length * m_bakeFrame);
                var gpuSkinningAnimation = new GPUSkinning_Animation
                {
                    fps = m_bakeFrame,
                    animType = clipId,
                    animName = animClip.name,
                    frameCount = frameCount,
                    frames = new GPUSkinning_BoneAnimationFrame[frameCount],
                    length = animClip.length
                };

                var mountPoints = GetMountPoints(obj);
                var mountPointsLength = mountPoints.Length;
                gpuSkinningAnimation.mountPoints = new GPUSkinning_BoneTransform[mountPointsLength];
                for (int i = 0; i < mountPointsLength; i++)
                {
                    var mountPoint = new GPUSkinning_BoneTransform();
                    gpuSkinningAnimation.mountPoints[i] = mountPoint;
                    mountPoint.positions = new Vector3[frameCount];
                    mountPoint.rotations = new Vector3[frameCount];
                }

                gpuSkinningAnimations.Add(gpuSkinningAnimation);
                var show = false;
                for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex)
                {
                    var frame = new GPUSkinning_BoneAnimationFrame();
                    gpuSkinningAnimation.frames[frameIndex] = frame;
                    List<Matrix4x4> matrices = new List<Matrix4x4>();
                    animClip.SampleAnimation(obj, (float)frameIndex / gpuSkinningAnimation.fps);
                    for (int i = 0; i < numBones; i++)
                    {
                        var bone = bones[i];
                        var boneTransform = bone.transform;

                        for (int j = 0; j < mountPointsLength; j++)
                        {
                            if (mountPoints[j] == boneTransform.name)
                            {
                                gpuSkinningAnimation.mountPoints[j].positions[frameIndex] = boneTransform.position;
                                gpuSkinningAnimation.mountPoints[j].rotations[frameIndex] = boneTransform.eulerAngles;

                                break;
                            }
                        }

                        Matrix4x4 matrixBip001 = bone.transform.localToWorldMatrix;
                        matrices.Add(matrixBip001);
                    }

                    frame.matrices = matrices.ToArray();
                    /*if (!show)
                    {
                        show = true;
                        var newObj = GameObject.Instantiate(obj);
                        newObj.name = animClip.name;
                        newObj.transform.position = new Vector3(clipId, 0, 0);
                    }*/
                }

                gpuSkinningAnimation.events = new GPUSkinning_BoneAnimationEvent[animClip.events.Length];
                for (var i = 0; i < animClip.events.Length; i++)
                {
                    var animationEvent = animClip.events[i];
                    gpuSkinningAnimation.events[i] = new GPUSkinning_BoneAnimationEvent
                    {
                        time = animationEvent.time,
                        functionName = animationEvent.functionName,
                        intParameter = animationEvent.intParameter
                    };
                }
            }

            allAnimation.animations = gpuSkinningAnimations.ToArray();

            for (int i = 0; i < allAnimation.animations.Length; i++)
            {
                allAnimation.animations[i].frameStartIndex = allAnimation.totalAnimationLength;
                allAnimation.totalAnimationLength += allAnimation.animations[i].frames.Length;
            }
            
            int maxSize = 2048;
            if (GpuSkinningAnimatorCreator.isSurvivor)
            {
                maxSize = 256;
            }
            int height = (allAnimation.totalAnimationLength + maxSize - 1) / maxSize * numBones * 2;
            var tex2D = new Texture2D(maxSize, height, TextureFormat.RGBAHalf, false);
            tex2D.filterMode = FilterMode.Point;
            int currentFrameIndex = 0;
            int animationIndex = 0;
            foreach (var boneAnimation in allAnimation.animations)
            {
                for (int frameIndex = 0; frameIndex < boneAnimation.frames.Length; frameIndex++)
                {
                    for (int boneIndex = 0; boneIndex < numBones; boneIndex++)
                    {
                        var matrix = boneAnimation.frames[frameIndex].matrices[boneIndex] * bones[boneIndex].bindpose;

                        Vector3 scale = matrix.lossyScale;
                        float sx = Mathf.Floor(scale.x * 100.0f);
                        float sy = Mathf.Floor(scale.y * 100.0f);
                        float sz = Mathf.Floor(scale.z * 100.0f);
                        if ((sx - sy) > 5.0f
                            || (sx - sz) > 5.0f
                            || (sy - sz) > 5.0f)
                        {
                            string strLog = string.Format("AnimClip scale X Y Z not equal: {0} -> {1} {2}", obj.name, boneAnimation.animName, bones[boneIndex].transform.name);
                            Warning("AnimClip scale", strLog);
                        }

                        Quaternion rotation = ToQuaternion(matrix);
                        int x = currentFrameIndex % maxSize;
                        int y = currentFrameIndex / maxSize;

                        var pixelX = x;
                        var pixelY = y * numBones * 2 + boneIndex * 2 + 1;
                        if (pixelX >= maxSize || pixelY >= height)
                        {
                            BattleDebug.LogError(assetName + " Error: " + pixelX + " " + pixelY + " " + maxSize + " " + height);
                        }
                        tex2D.SetPixel(x, y * numBones * 2 + boneIndex * 2, new Color(rotation.x, rotation.y, rotation.z, rotation.w));
                        tex2D.SetPixel(x, y * numBones * 2 + boneIndex * 2 + 1, new Color(matrix.GetColumn(3).x, matrix.GetColumn(3).y, matrix.GetColumn(3).z, Mathf.Clamp01(matrix.lossyScale.magnitude)));
                    }
                    currentFrameIndex++;
                }
                animationIndex++;
            }
            //UnityEngine.Debug.LogError(allAnimation.animations.Length + " " + numBones + " " + currentFrameIndex + " " + allAnimation.totalAnimationLength);
            tex2D.Apply();

            byte[] bytes = tex2D.GetRawTextureData();
            string textureDataPath = $"{textureDataDirectory}/{assetName}.bytes";
            File.WriteAllBytes(textureDataPath, bytes);
            allAnimation.textureDataPath = textureDataPath;
            allAnimation.textureWidth = maxSize;
            allAnimation.textureHeight = height;

            string texPath = animationDirectory + "/" + assetName + ".png";
            var bytes2 = tex2D.EncodeToPNG();
            File.WriteAllBytes(texPath, bytes2);

            foreach (var boneAnimation in allAnimation.animations)
            {
                boneAnimation.frames = null;
            }

            string allAnimationAssetPath = Path.Combine(configDirectory, assetName) + ".asset";
            UnityEditor.AssetDatabase.CreateAsset(allAnimation, allAnimationAssetPath);
            Object.DestroyImmediate(tex2D);
            UnityEditor.AssetDatabase.Refresh();
        }

    }
}
